package com.zzxx.wechat.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.zzxx.wechat.adpater.ArcteryxTaskAdapter;
import com.zzxx.wechat.common.vo.ArcteryxCommodityVo;
import com.zzxx.wechat.common.vo.ArcteryxLinkVo;
import com.zzxx.wechat.common.vo.ArcteryxNature;
import com.zzxx.wechat.common.vo.ArcteryxSpuInfoVo;
import com.zzxx.wechat.dao.ArcteryxLinkDao;
import com.zzxx.wechat.dao.ArcteryxSkuDao;
import com.zzxx.wechat.entity.ArcteryxLink;
import com.zzxx.wechat.entity.ArcteryxSku;
import com.zzxx.wechat.exception.DistributedLockException;
import com.zzxx.wechat.service.ArcteryxLinkService;
import com.zzxx.wechat.service.ArcteryxService;
import com.zzxx.wechat.util.HttpProxyClient;
import com.zzxx.wechat.util.MdcCompletableFuture;
import com.zzxx.wechat.util.TraceUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ArcteryxLinkServiceImpl implements ArcteryxLinkService {

    @Resource
    private ArcteryxLinkDao arcteryxLinkDao;
    @Resource
    private ArcteryxSkuDao arcteryxSkuDao;
    @Resource
    private ArcteryxService arcteryxService;
    @Resource
    HttpProxyClient httpProxyClient;

    /**
     * 专门用于处理 Arcteryx 链接查询任务的线程池
     */
    @Autowired
    private ThreadPoolExecutor arcteryxLinkQueryExector;

    /**
     * 用于并行处理商品链接的线程池
     * 处理同时查询多个商品编号对应的链接信息
     */
    @Autowired
    private ThreadPoolExecutor arcteryxParallelProcessingExecutor;

    private static final List<ArcteryxNature> ARCTERYX_NATURE_LIST = new ArrayList<>();

    static {

        ARCTERYX_NATURE_LIST.add(new ArcteryxNature("us", "en"));
        ARCTERYX_NATURE_LIST.add(new ArcteryxNature("us", "jp"));
        ARCTERYX_NATURE_LIST.add(new ArcteryxNature("ca", "en"));
        ARCTERYX_NATURE_LIST.add(new ArcteryxNature("ca", "jp"));
        ARCTERYX_NATURE_LIST.add(new ArcteryxNature("it", "en"));
        ARCTERYX_NATURE_LIST.add(new ArcteryxNature("it", "jp"));

    }

    private static final String PRODUCT_URL_TEMPLATE = "https://arcteryx.com/%s/%s/shop/%s";

    // 错误消息常量
    private static final String ERROR_EMPTY_HTML = "解析URL返回为空";
    private static final String ERROR_URL_NOT_EXIST = "该URL不存在,You’ve strayed off trail";
    private static final String ERROR_NO_PRODUCT = "该URL中不存在商品";

    /**
     * 用于存储每个productNo对应的锁
     */
    private final Map<String, ReentrantLock> productNoLocks = new ConcurrentHashMap<>();

    /**
     * 根据商品编号获取锁
     */
    private ReentrantLock getLockForProductNo(String productNo) {
        return productNoLocks.computeIfAbsent(productNo, k -> new ReentrantLock());
    }

    @Override
    public List<ArcteryxCommodityVo> parallelQueryProductLinks(List<String> productNoList) {
        List<ArcteryxCommodityVo> result = new CopyOnWriteArrayList<>();
        CountDownLatch countDownLatch = new CountDownLatch(productNoList.size());
        String traceId = MDC.get(TraceUtil.TRACE_ID);

        for (String productNo : productNoList) {
            arcteryxLinkQueryExector.execute(() -> {
                try (MDC.MDCCloseable ignored = MDC.putCloseable(TraceUtil.TRACE_ID, traceId + "_" + productNo)) {
                    ArcteryxCommodityVo arcteryxCommodityVo = new ArcteryxCommodityVo();
                    arcteryxCommodityVo.setProductNo(productNo);
                    arcteryxCommodityVo.setArcteryxLinkVos(Collections.emptyList());
                    List<ArcteryxLinkVo> arcteryxLinkVos = queryLinkByProductNo(productNo);

                    if (CollectionUtil.isNotEmpty(arcteryxLinkVos)) {
                        ArcteryxLinkVo basicArcteryxLinkVo = arcteryxLinkVos.get(0);
                        arcteryxCommodityVo.setProductName(basicArcteryxLinkVo.getAnalyticsName());
                        arcteryxCommodityVo.setArcteryxLinkVos(arcteryxLinkVos);
                    }

                    result.add(arcteryxCommodityVo);
                } finally {
                    countDownLatch.countDown();
                }
            });
        }

        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            log.warn("等待所有商品链接查询完成时中断", e);
            Thread.currentThread().interrupt(); // 重新设置中断标志
        }
        result.sort(Comparator.comparing(ArcteryxCommodityVo::getProductNo));
        return result;
    }

    public List<ArcteryxLinkVo> queryLinkByProductNo(String productNo) {
        ReentrantLock lock = getLockForProductNo(productNo);


        try {
            if (!lock.tryLock(30, TimeUnit.SECONDS)) {
                log.error("商品:" + productNo + "获取锁失败");
                throw new DistributedLockException("商品:" + productNo + "获取锁超时");
            }
            List<CompletableFuture<ArcteryxLinkVo>> futures = new ArrayList<>();
            for (ArcteryxNature arcteryxNature : ARCTERYX_NATURE_LIST) {

                CompletableFuture<ArcteryxLinkVo> future = MdcCompletableFuture.supplyAsync(() -> processLink(productNo, arcteryxNature),
                        arcteryxParallelProcessingExecutor
                ).exceptionally(this::handleProcessingException);
                futures.add(future);
            }
            return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
                    .thenApply(v -> futures.stream().map(CompletableFuture::join)
                            .filter(Objects::nonNull)
                            .collect(Collectors.toList())).join();
        } catch (Exception e) {
            log.error("调用链接查询商品:{}库存发生异常", productNo, e);
            return Collections.emptyList();
        } finally {
            lock.unlock();
        }
    }

    @Override
    public List<ArcteryxLinkVo> processExistingLinks(List<String> productNoList) {
        List<ArcteryxLink> arcteryxLinks = arcteryxLinkDao.queryBySpuIds(productNoList);
        if (CollectionUtil.isEmpty(arcteryxLinks)) {
            return Collections.emptyList();
        }
        List<Integer> arcteryxLinkIds = arcteryxLinks.stream().map(ArcteryxLink::getId).collect(Collectors.toList());

        List<ArcteryxSku> arcteryxSkuList = arcteryxSkuDao.queryByLinkIds(arcteryxLinkIds);
        Map<Integer, List<ArcteryxSku>> arcteryxSkuGroup = new HashMap<>();
        for (ArcteryxSku arcteryxSku : arcteryxSkuList) {
            if (!arcteryxSkuGroup.containsKey(arcteryxSku.getLinkId())) {
                arcteryxSkuGroup.put(arcteryxSku.getLinkId(), new ArrayList<>());
            }
            arcteryxSkuGroup.get(arcteryxSku.getLinkId()).add(arcteryxSku);
        }

        List<ArcteryxLinkVo> result = new ArrayList<>();
        for (ArcteryxLink arcteryxLink : arcteryxLinks) {
            List<ArcteryxSku> skus = arcteryxSkuGroup.get(arcteryxLink.getId());
            result.add(ArcteryxTaskAdapter.arcteryxLink2Vo(arcteryxLink, null));
        }

        result.sort(Comparator.comparing(ArcteryxLinkVo::getSpuId));
        return result;
    }

    private ArcteryxLinkVo processLink(String productNo, ArcteryxNature arcteryxNature) {
        long startTime = System.currentTimeMillis();

        String url = String.format(PRODUCT_URL_TEMPLATE, arcteryxNature.getCountryCode(), arcteryxNature.getLanguageCode(), productNo);

        ArcteryxLink arcteryxLink = arcteryxLinkDao.queryByUrl(url);
        if (arcteryxLink == null) {
            arcteryxLink = generateArcteryLink(productNo, arcteryxNature, url);
        }

        log.info("请求三方获取库存开始,商品:{},URL:{}", productNo, url);
        String htmlStr = httpProxyClient.doGetIgnoreSsl(url);
        log.info("请求三方获取库存结束,商品:{},URL:{},耗时:{}ms", productNo, url, System.currentTimeMillis() - startTime);

        if (handleCommonErrorCases(htmlStr, arcteryxLink, url)) {
            return ArcteryxTaskAdapter.arcteryxLink2Vo(arcteryxLink);
        }

        // 使用 Jsoup 解析 HTML
        Document document = Jsoup.parse(htmlStr);
        Elements element = document.select("#__NEXT_DATA__");
        String html = element.html();

        if (StringUtils.isEmpty(html)) {
            log.info("该URL中不存在商品,链接:{},返回htmlStr:{}", url, htmlStr);
            arcteryxLinkDao.updateReasonById(arcteryxLink.getId(), ERROR_NO_PRODUCT);
            return ArcteryxTaskAdapter.arcteryxLink2Vo(arcteryxLink);
        }

        ArcteryxSpuInfoVo arcteryxSpuInfoVo = arcteryxService.analysisProduct(arcteryxLink, html);

        log.info("解析URL:{},返回数据:{},耗时:{}", url, JSON.toJSONString(arcteryxSpuInfoVo), System.currentTimeMillis() - startTime);

        return ArcteryxTaskAdapter.arcteryxLink2Vo(arcteryxLink, arcteryxSpuInfoVo);
    }


    // 提取公共方法处理常见的错误检查
    private boolean handleCommonErrorCases(String htmlStr, ArcteryxLink arcteryxLink, String url) {
        if (StringUtils.isEmpty(htmlStr)) {
            log.info("解析URL返回为空,链接:{}", url);
            arcteryxLinkDao.updateReasonById(arcteryxLink.getId(), ERROR_EMPTY_HTML);
            return true;
        }

        if (htmlStr.contains("You’ve strayed off trail")) {
            log.info("该URL不存在,You’ve strayed off trail,链接:{}", url);
            arcteryxLinkDao.updateReasonById(arcteryxLink.getId(), ERROR_URL_NOT_EXIST);
            return true;
        }

        return false;
    }


    private ArcteryxLink generateArcteryLink(String productNo, ArcteryxNature arcteryxNature, String url) {
        ArcteryxLink arcteryxLink = new ArcteryxLink();
        arcteryxLink.setSpuId(productNo);
        arcteryxLink.setCountryCode(arcteryxNature.getCountryCode());
        arcteryxLink.setLanguageCode(arcteryxNature.getLanguageCode());
        arcteryxLink.setUrl(url);
        arcteryxLinkDao.save(arcteryxLink);
        return arcteryxLink;
    }


    private ArcteryxLinkVo handleProcessingException(Throwable ex) {

        log.info("查询三方库存发生异常:", ex);
        return null;
    }


}
